package com.atguigu.gmall0218.payment.controller.third;

import com.atguigu.gmall0218.bean.PaymentInfo;
import com.atguigu.gmall0218.bean.PaymentInfoException;
import com.atguigu.gmall0218.bean.enums.PayOrg;
import com.atguigu.gmall0218.bean.enums.PaymentStatus;
import com.atguigu.gmall0218.payment.config.WxConfig;
import com.atguigu.gmall0218.payment.constant.ExceptCodeConstants;
import com.atguigu.gmall0218.payment.service.impl.PayCenterCombSVImpl;
import com.atguigu.gmall0218.payment.service.impl.PaymentExceptionServiceImpl;
import com.atguigu.gmall0218.payment.service.impl.PaymentServiceImpl;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

/**
 * <pre>
 *    @author  : liquid
 *    email   : xiaokui.li@guigu.com
 *    desc    : 微信支付
 *    version : v1.0
 * </pre>
 */
@Controller
@RequestMapping("weixin/")
@Slf4j
public class WxController {
    @Autowired
    private PaymentServiceImpl paymentSerivce;
    @Autowired
    private PayCenterCombSVImpl payCenterCombSV;
    @Autowired
    private PaymentExceptionServiceImpl paymentExceptionService;
    /**
     * 返回成功xml
     */
    private final String resSuccessXml =
            "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
    /**
     * 返回失败xml
     */
    private final String resFailXml =
            "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml>";
    @RequestMapping("wxCode")
    @ResponseBody
    public Map pay(HttpServletRequest request, HttpServletResponse response) {
        String orderId = request.getParameter("orderId");
        Map map = paymentSerivce.createNative(orderId,"1");
        return map;
    }
    /**
     * 该链接是通过【统一下单API】中提交的参数notify_url设置，如果链接无法访问，商户将无法接收到微信通知。
     * 通知url必须为直接可访问的url，不能携带参数。示例：notify_url：“https://pay.weixin.qq.com/wxpay/pay.action”
     * <p>
     * 支付完成后，微信会把相关支付结果和用户信息发送给商户，商户需要接收处理，并返回应答。
     * 对后台通知交互时，如果微信收到商户的应答不是成功或超时，微信认为通知失败，微信会通过一定的策略定期重新发起通知，尽可能提高通知的成功率，但微信不保证通知最终能成功。
     * （通知频率为15/15/30/180/1800/1800/1800/1800/3600，单位：秒）
     * 注意：同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
     * 推荐的做法是，当收到通知进行处理时，首先检查对应业务数据的状态，判断该通知是否已经处理过，如果没有处理过再进行处理，如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
     * 特别提醒：商户系统对于支付结果通知的内容一定要做签名验证，防止数据泄漏导致出现“假通知”，造成资金损失
     *
     * @author lxk
     * @date 2019/10/11
     */
    @RequestMapping("/wxnotify")
    public void wxnotify(HttpServletRequest request, HttpServletResponse response) {
        String resXml = "";
        InputStream inStream;
        try {
            inStream = request.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            log.info("wxnotify:微信支付----start----");
            // 获取微信调用我们notify_url的返回信息
            String result = new String(outSteam.toByteArray(), "utf-8");
            log.info("wxnotify:微信支付----result----=" + result);
            // 关闭流
            outSteam.close();
            inStream.close();
            // xml转换为map
            Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
            if (WXPayConstants.SUCCESS.equalsIgnoreCase(resultMap.get("result_code"))) {
                //todo 并校验返回的订单金额是否与商户侧的订单金额一致
                log.info("wxnotify:微信支付----返回成功");
                if (WXPayUtil.isSignatureValid(resultMap, WxConfig.partnerkey)) {
                    log.info("wxnotify:微信支付----验证签名成功");
                    // 付款记录修改 & 记录付款日志<transaction_id><![CDATA[4200000381201909188023383778]]></transaction_id>
                    PaymentInfo paymentInfoQuery = new PaymentInfo();
                    String out_trade_no = resultMap.get("out_trade_no");
                    String transaction_id = resultMap.get("transaction_id");
                    paymentInfoQuery.setOutTradeNo(out_trade_no);
                    PaymentInfo paymentInfo = paymentSerivce.getPaymentInfo(paymentInfoQuery);
                    if (paymentInfo.getPaymentStatus()== PaymentStatus.PAID || paymentInfo.getPaymentStatus()==PaymentStatus.ClOSED){
                        // 判断是否是同一个第三方流水号，如果是，则不操作，否则存入异常单
                        if (transaction_id.equals(paymentInfo.getPayTradeNo())){
                            //表示，这是微信重复发送的异步通知，支付中心不做任何处理
                            resXml = resSuccessXml;
                        }else {
                            //存入异常单，表示是多种支付方式的重复支付
                            PaymentInfoException paymentInfoException = new PaymentInfoException();
                            this.savePayException(out_trade_no, paymentInfo, paymentInfoException);
                            resXml = resSuccessXml;
                            //todo 发送异常消息通知退款接口，随便选择其中一个即可 或者显示退款按钮等等，主要看产品设计
                        }
                        responseWx(response, resXml);
                    }
                    // 更新交易记录的状态！
                    payCenterCombSV.updatePayCenter(out_trade_no,transaction_id);
                    //todo 发送消息修改订单状态
                    // 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                    resXml = resSuccessXml;

                } else {
                    log.error("wxnotify:微信支付----判断签名错误");
                }

            } else {
                log.error("wxnotify:支付失败,错误信息：" + resultMap.get(ExceptCodeConstants.WX_PAY_FAIL));
                resXml = resFailXml;
            }

        } catch (Exception e) {
            log.error("wxnotify:支付回调发布异常：", e);
        } finally {
            responseWx(response, resXml);
        }

    }

    private void responseWx(HttpServletResponse response, String resXml) {
        try {
            // 处理业务完毕
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        } catch (IOException e) {
            log.error("wxnotify:支付回调发布异常:out：", e);
        }
    }

    /**
     * 保存异常单
     * @param out_trade_no 第三方流水号
     * @param paymentInfo 支付信息
     * @param paymentInfoException  支付异常单信息
     */
    private void savePayException(String out_trade_no, PaymentInfo paymentInfo, PaymentInfoException paymentInfoException) {
        paymentInfoException.setOrderId(paymentInfo.getOrderId());
        paymentInfoException.setOutTradeNo(out_trade_no);
        paymentInfoException.setPayOrgId(PayOrg.WX.getPayName());
        paymentInfoException.setPayTradeNo(paymentInfo.getPayTradeNo());
        paymentInfoException.setSubject(paymentInfo.getSubject());
        paymentInfoException.setTotalAmount(paymentInfo.getTotalAmount());
        paymentInfoException.setPaymentStatus(PaymentStatus.REPEAT);
        paymentExceptionService.savePaymentInfoException(paymentInfoException);
    }


}